#define DECODE_TEST_C
#define _DEFAULT_SOURCE 1
#if defined(__linux__)
#  define _GNU_SOURCE 1 /* for memmem on linux */
#endif
#define _BSD_SOURCE 1
#ifdef __STDC_ALLOC_LIB__
#  define __STDC_WANT_LIB_EXT2__ 1 /* for strdup */
#else
#  define _USE_BSD 1
#endif

#define IS_DECODER
#include <stdlib.h>
#include "../../src/common.h"
// CLANG_DIAG_IGNORE (-Wpragma-pack)
#include "decode.c"
// CLANG_DIAG_RESTORE
#include "tests_common.h"

static void
read_literal_length_tests (void)
{
  struct _test
  {
    const char *bits;
    unsigned char opcode;
    int result;
  } tests[] = {
    { "00000101", 5, 8 },
    { "00000001", 14, 17 },
    { "1000000010000000", 0xf0, 146 },
  };
  for (int i = 0; i < ARRAY_SIZE (tests); i++)
    {
      Bit_Chain bitchain = strtobt (tests[i].bits);
      int result = read_literal_length (&bitchain, tests[i].opcode);
      if (result == tests[i].result)
        {
          if (i == ARRAY_SIZE (tests) - 1)
            ok ("read_literal_length");
          else
            pass ();
        }
      else
        fail ("read_literal_length [%d]: %d", i, result);
      bitfree (&bitchain);
    }
}

static void
read_compressed_bytes_tests (void)
{
  struct _test
  {
    const char *bits;
    unsigned char opcode;
    unsigned mask;
    int result;
  } tests[] = {
    { "11011101", 5, 7, 7 },
    { "11011101", 5, 1, 3 },
    { "0000000010000000", 4, 1, 386 },
  };
  for (int i = 0; i < ARRAY_SIZE (tests); i++)
    {
      Bit_Chain bitchain = strtobt (tests[i].bits);
      int result
          = read_compressed_bytes (&bitchain, tests[i].opcode, tests[i].mask);
      if (result == tests[i].result)
        {
          if (i == ARRAY_SIZE (tests) - 1)
            ok ("read_compressed_bytes");
          else
            pass ();
        }
      else
        fail ("read_compressed_bytes [%d]: %d", i, result);
      bitfree (&bitchain);
    }
}

static void
two_byte_offset_tests (void)
{
  struct _test
  {
    const char *bits;
    int offset;
    BITCODE_RC result;
  } tests[] = {
    { "1111000000000111", 509, 0xf0 },
    { "1111000000000110", 445, 0xf0 },
    { "1100000000000110", 433, 0xc0 },
  };
  for (int i = 0; i < ARRAY_SIZE (tests); i++)
    {
      Bit_Chain bitchain = strtobt (tests[i].bits);
      int offset = 0;
      BITCODE_RC result = two_byte_offset (&bitchain, 1, &offset);
      if (result == tests[i].result && offset == tests[i].offset)
        {
          if (i == ARRAY_SIZE (tests) - 1)
            ok ("two_byte_offset");
          else
            pass ();
        }
      else
        fail ("two_byte_offset [%d]: %d, 0x%x", i, offset, result);
      bitfree (&bitchain);
    }
}

static void
decompress_R2004_section_tests (void)
{
  int result;
  static Bit_Chain src, dec = { 0 };
  // from example_2004 via DEBUG
  unsigned char comp_auxh_bin[225] = {
    // very bad compression indeed
    0x00, 0x01, 0xff, 0x88, 0x01, 0x21, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x00,
    0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x50, 0x00, 0x01,
    0x16, 0x00, 0x2e, 0x00, 0x5c, 0x00, 0x01, 0x04, 0x00, 0x65, 0x05, 0x5f,
    0x03, 0x02, 0x00, 0x01, 0x58, 0x01, 0xbc, 0x00, 0x00, 0x07, 0x01, 0x00,
    0x00, 0x02, 0x00, 0x07, 0x00, 0xea, 0x74, 0x25, 0x00, 0x9a, 0xe6, 0x33,
    0x04, 0xb0, 0x82, 0x25, 0x00, 0xe0, 0x1c, 0xf7, 0x01, 0xe9, 0x0b, 0x9a,
    0x08, 0x0d, 0x00, 0x54, 0x08, 0x94, 0x03, 0x5c, 0x17, 0x5d, 0x02, 0x11,
    0x50, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0x0c,
    0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00
  };
  unsigned char decomp_auxh_bin[123]
      = { 0xff, 0x88, 0x01, 0x21, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x00, 0x00,
          0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x16, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x2e, 0x00, 0x04, 0x00,
          0x65, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0xea, 0x74, 0x25,
          0x00, 0x9a, 0xe6, 0x33, 0x04, 0xb0, 0x82, 0x25, 0x00, 0xe0, 0x1c,
          0xf7, 0x01, 0xe9, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00 };
  unsigned char comp_ofs_bin[181]
      = { 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xb0,
          0x82, 0x25, 0x00, 0xe0, 0x1c, 0xf7, 0x01, 0x00, 0x00, 0x00, 0x00,
          0x04, 0x32, 0x54, 0x01, 0x01, 0x00, 0x00, 0x00, 0x64, 0x8e, 0x01,
          0x00, 0x02, 0x70, 0x02, 0x01, 0xff, 0xff, 0xff, 0xff, 0x74, 0x02,
          0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x14, 0x00, 0x0a,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x11, 0x00, 0x00 };
  unsigned char decomp_ofs_bin[53]
      = { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x82, 0x25,
          0x00, 0xe0, 0x1c, 0xf7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x32,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };

  src.chain = comp_auxh_bin;
  src.size = sizeof comp_auxh_bin;
  src.bit = 0;
  src.byte = 0UL;
  bit_chain_alloc_size (&dec, sizeof decomp_auxh_bin);
  dec.size = sizeof decomp_auxh_bin;
  dec.bit = 0;
  dec.byte = 0UL;
  result = decompress_R2004_section (&src, &dec);
  if (result == 0 && dec.size == sizeof decomp_auxh_bin
      && memcmp (dec.chain, decomp_auxh_bin, sizeof decomp_auxh_bin) == 0)
    pass ();
  else
    fail ("decompress_R2004_section auxh %d %lu", result,
          (unsigned long)dec.size);

  src.chain = comp_ofs_bin;
  src.size = sizeof comp_ofs_bin;
  src.byte = 0;
  bit_chain_alloc_size (&dec, sizeof decomp_ofs_bin);
  dec.size = sizeof decomp_ofs_bin;
  dec.byte = 0;
  result = decompress_R2004_section (&src, &dec);
  if (result == 0
      && memcmp (dec.chain, decomp_ofs_bin, sizeof decomp_ofs_bin) == 0)
    ok ("decompress_R2004_section");
  else
    fail ("decompress_R2004_section ofs %d %lu", result,
          (unsigned long)dec.size);
}

int
main (int argc, char const *argv[])
{
  loglevel = is_make_silent () ? 0 : 2;

  read_literal_length_tests ();
  read_compressed_bytes_tests ();
  two_byte_offset_tests ();
  decompress_R2004_section_tests ();

  return 0;
}
